﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace YmtAuth.Common.Cache
{
    public class LocalCache2<TKey, TValue>
    {
        private ConcurrentDictionary<TKey, CacheItem> cache;
        private readonly int max;
        private int count;

        public LocalCache2(int max=100000)
        {
            this.cache = new ConcurrentDictionary<TKey, CacheItem>();
            this.max = max;
        }

        public void Add(TKey key, TValue value, TimeSpan timeOut)
        {           
            RemoveGTMaxCache();           
            var success = cache.TryAdd(key, new CacheItem() {Value = value,UseCount = 1, ExpiredTime = DateTime.Now.Add(timeOut)});
            if (success)
                Interlocked.Increment(ref count);
        }

        public int CacheCount
        {
            get { return count; }
        }

        public void Replace(TKey key, TValue value,TimeSpan timeOut)
        {
            var item = new CacheItem {Value = value, ExpiredTime = DateTime.Now.Add(timeOut)};
            cache.AddOrUpdate(key, k => item, (k, v) => item);
        }

        public TValue GetOrAdd(TKey key, Func<TKey, _CacheItem> notfindfFunc, bool returnTimeOutItem = true)
        {
            CacheItem item;
            if (!cache.TryGetValue(key, out item))
            {
                return AddNewItem(key, notfindfFunc);
            }
            //过期
            if (item.ExpiredTime < DateTime.Now)
            {
                if (cache.TryRemove(key, out item))
                {
                    Interlocked.Decrement(ref count);
                }
                if (returnTimeOutItem)
                    return item.Value;
                else
                    return AddNewItem(key, notfindfFunc);
            }
            item.UseCount += 1;
            return item.Value;
        }

        private TValue AddNewItem(TKey key, Func<TKey, _CacheItem> notfindfFunc)
        {
            _CacheItem newItem = default(_CacheItem);
            try
            {
               newItem = notfindfFunc(key);
            }
            catch (Exception)
            {
              //TODO
            }           
            if (newItem.Value == null)
                return default(TValue);
            RemoveGTMaxCache();
            var success = cache.TryAdd(key,
                new CacheItem()
                {
                    Value = newItem.Value,
                    UseCount = 1,
                    ExpiredTime = DateTime.Now.Add(newItem.ExpiredTime)
                });
            if (success)
                Interlocked.Increment(ref count);
            return newItem.Value;
        }

        //移除大于上限的缓存项
        private void RemoveGTMaxCache()
        {
            if (count <= max) return;
            //查找过期的缓存项目
            var expiredKeys = cache.Where(k => k.Value.ExpiredTime <= DateTime.Now).Select(k => k.Key);
            if (expiredKeys.Any())
            {
                foreach (var k in expiredKeys)
                {
                    CacheItem item;
                    cache.TryRemove(k, out item);
                }
                Interlocked.Add(ref count, -expiredKeys.Count());
            }
            else
            {
                var uesCountKey = cache.First(k => k.Value.UseCount <= 1).Key;

                CacheItem item;
                cache.TryRemove(uesCountKey, out item);

                Interlocked.Add(ref count, -1);
            }
        }

        public struct _CacheItem
        {
            public TValue Value { get; set; }
            public TimeSpan ExpiredTime { get; set; }
        }

        internal class CacheItem
        {
            public TValue Value { get; set; }
            public int UseCount { get; set; }
            public DateTime ExpiredTime { get; set; }
        }
    }
}
